home *** CD-ROM | disk | FTP | other *** search
/ Internet Info 1994 March / Internet Info CD-ROM (Walnut Creek) (March 1994).iso / networking / ip / ka9q / MNetsrc.hqx / Mac TCP_IP Source v.33 / ftpcli.c < prev    next >
Encoding:
C/C++ Source or Header  |  1989-04-01  |  16.5 KB  |  824 lines

  1. /* FTP client (interactive user) code */
  2. #include <stdio.h>
  3. #include "global.h"
  4. #include "mbuf.h"
  5. #include "netuser.h"
  6. #include "icmp.h"
  7. #include "timer.h"
  8. #include "tcp.h"
  9. #ifdef MAC
  10. #include "mac_files.h"
  11. #include "MacBinary.h"
  12. #endif
  13. #include "ftp.h"
  14. #include "session.h"
  15. #include "cmdparse.h"
  16.  
  17. extern struct session *current;
  18. extern char nospace[];
  19. extern char badhost[];
  20. static char notsess[] = "Not an FTP session!\n";
  21. static char cantwrite[] = "Can't write %s\n";
  22. static char cantread[] = "Can't read %s\n";
  23. #ifdef MAC
  24. MBFile *MBopen();
  25. #endif
  26.  
  27. int donothing(),doftpcd(),dolist(),doget(),dols(),doput(),dotype(),doabort(),
  28. #ifdef MAC
  29.     domkdir(),dormdir(),domacb();
  30. #else
  31.     domkdir(),dormdir();
  32. #endif
  33.  
  34. struct cmds ftpabort[] = {
  35.     "",        donothing,    0,    NULLCHAR,        NULLCHAR,
  36.     "abort",    doabort,    0,    NULLCHAR,        NULLCHAR,
  37.     NULLCHAR,    NULLFP,        0,    "Only valid command is \"abort\"", NULLCHAR,
  38. };
  39.  
  40. struct cmds ftpcmds[] = {
  41.     "",        donothing,    0,    NULLCHAR,        NULLCHAR,
  42.     "cd",        doftpcd,    2,    "cd <directory>",    NULLCHAR,
  43.     "dir",        dolist,        0,    NULLCHAR,        NULLCHAR,
  44.     "list",        dolist,        0,    NULLCHAR,        NULLCHAR,
  45.     "get",        doget,        2,    "get remotefile <localfile>",    NULLCHAR,
  46.     "ls",        dols,        0,    NULLCHAR,        NULLCHAR,
  47. #ifdef MAC
  48.     "macb",        domacb,        0,    NULLCHAR,        NULLCHAR,
  49. #endif
  50.     "mkdir",    domkdir,    2,    "mkdir <directory>",    NULLCHAR,
  51.     "nlst",        dols,        0,    NULLCHAR,        NULLCHAR,
  52.     "rmdir",    dormdir,    2,    "rmdir <directory>",    NULLCHAR,
  53.     "put",        doput,        2,    "put localfile <remotefile>",    NULLCHAR,
  54.     "type",        dotype,        0,    NULLCHAR,        NULLCHAR,
  55.     NULLCHAR,    NULLFP,        0,     NULLCHAR,        NULLCHAR,
  56. };
  57.  
  58. /* Handle top-level FTP command */
  59. doftp(argc,argv)
  60. int argc;
  61. char *argv[];
  62. {
  63.     int32 resolve();
  64.     int ftpparse();
  65.     char *inet_ntoa();
  66.     void ftpccr(),ftpccs();
  67.     struct session *s;
  68.     struct ftp *ftp,*ftp_create();
  69.     struct tcb *tcb;
  70.     struct socket lsocket,fsocket;
  71.  
  72.     lsocket.address = ip_addr;
  73.     lsocket.port = lport++;
  74.     if((fsocket.address = resolve(argv[1])) == 0){
  75.         printf(badhost,argv[1]);
  76.         return 1;
  77.     }
  78.     if(argc < 3)
  79.         fsocket.port = FTP_PORT;
  80.     else
  81.         fsocket.port = atoi(argv[2]);
  82.  
  83.     /* Allocate a session control block */
  84.     if((s = newsession()) == NULLSESSION){
  85.         printf("Too many sessions\n");
  86.         return 1;
  87.     }
  88.     current = s;
  89.     if((s->name = malloc((unsigned)strlen(argv[1])+1)) != NULLCHAR)
  90.         strcpy(s->name,argv[1]);
  91.     s->type = FTP;
  92.     s->parse = ftpparse;
  93.  
  94.     /* Allocate an FTP control block */
  95.     if((ftp = ftp_create(0)) == NULLFTP){
  96.         s->type = FREE;
  97.         printf(nospace);
  98.         return 1;
  99.     }
  100.     ftp->state = COMMAND_STATE;
  101.     s->cb.ftp = ftp;    /* Downward link */
  102.     ftp->session = s;    /* Upward link */
  103.  
  104.     /* Now open the control connection */
  105.     tcb = open_tcp(&lsocket,&fsocket,TCP_ACTIVE,
  106.         0,ftpccr,NULLVFP,ftpccs,0,(char *)ftp);
  107.     ftp->control = tcb;
  108.     go();
  109.     return 0;
  110. }
  111. /* Parse user FTP commands */
  112. int
  113. ftpparse(line,len)
  114. char *line;
  115. int16 len;
  116. {
  117.     struct mbuf *bp;
  118.  
  119.     if(current->cb.ftp->state != COMMAND_STATE){
  120.         /* The only command allowed in data transfer state is ABORT */
  121.         if(cmdparse(ftpabort,line) == -1){
  122. #ifdef MAC
  123.                if(current->cb.ftp->state != COMMAND_STATE) 
  124.                 printf("Transfer in progress; only ABORT is acceptable\n");
  125. #else
  126.             printf("Transfer in progress; only ABORT is acceptable\n");
  127. #endif
  128.         }
  129.         fflush(stdout);
  130.         return;
  131.     }
  132.  
  133.     /* Save it now because cmdparse modifies the original */
  134.     bp = qdata(line,len);
  135.  
  136.     if(cmdparse(ftpcmds,line) == -1){
  137.         /* Send it direct */
  138.         if(bp != NULLBUF)
  139.             send_tcp(current->cb.ftp->control,bp);
  140.         else
  141.             printf(nospace);
  142.     } else {
  143.         free_p(bp);
  144.     }
  145.     fflush(stdout);
  146. }
  147. /* Handle null line to avoid trapping on first command in table */
  148. static
  149. int
  150. donothing(argc,argv)
  151. int argc;
  152. char *argv[];
  153. {
  154. }
  155. /* Translate 'cd' to 'cwd' for convenience */
  156. static
  157. int
  158. doftpcd(argc,argv)
  159. int argc;
  160. char *argv[];
  161. {
  162.     register struct ftp *ftp;
  163.  
  164.     ftp = current->cb.ftp;
  165.     return sndftpmsg(ftp,"CWD %s\r\n",argv[1]);
  166. }
  167. /* Translate 'mkdir' to 'xmkd' for convenience */
  168. static
  169. int
  170. domkdir(argc,argv)
  171. int argc;
  172. char *argv[];
  173. {
  174.     register struct ftp *ftp;
  175.  
  176.     ftp = current->cb.ftp;
  177.     return sndftpmsg(ftp,"XMKD %s\r\n",argv[1]);
  178. }
  179. /* Translate 'rmdir' to 'xrmd' for convenience */
  180. static
  181. int
  182. dormdir(argc,argv)
  183. int argc;
  184. char *argv[];
  185. {
  186.     register struct ftp *ftp;
  187.  
  188.     ftp = current->cb.ftp;
  189.     return sndftpmsg(ftp,"XRMD %s\r\n",argv[1]);
  190. }
  191. /* Handle "type" command from user */
  192. static
  193. int
  194. dotype(argc,argv)
  195. int argc;
  196. char *argv[];
  197. {
  198.     register struct ftp *ftp;
  199.  
  200.     ftp = current->cb.ftp;
  201.     if(argc < 2){
  202.         switch(ftp->type){
  203.         case IMAGE_TYPE:
  204.             printf("Image\n");
  205.             break;
  206.         case ASCII_TYPE:
  207.             printf("Ascii\n");
  208.             break;
  209.         }
  210.         return 0;
  211.     }
  212.     switch(*argv[1]){
  213.     case 'i':
  214.     case 'b':
  215.         ftp->type = IMAGE_TYPE;
  216.         sndftpmsg(ftp,"TYPE I\r\n");
  217.         break;
  218.     case 'a':
  219.         ftp->type = ASCII_TYPE;
  220.         sndftpmsg(ftp,"TYPE A\r\n");
  221.         break;
  222.     case 'l':
  223.         ftp->type = IMAGE_TYPE;
  224.         sndftpmsg(ftp,"TYPE L %s\r\n",argv[2]);
  225.         break;
  226.     default:
  227.         printf("Invalid type %s\n",argv[1]);
  228.         return 1;
  229.     }
  230.     return 0;
  231. }
  232. /* Start receive transfer. Syntax: get <remote name> [<local name>] */
  233. static
  234. doget(argc,argv)
  235. int argc;
  236. char *argv[];
  237. {
  238.     void ftpdr(),ftpcds();
  239.     char *remotename,*localname;
  240.     register struct ftp *ftp;
  241.     char *mode;
  242. #ifdef MAC
  243.     char ftpname[256],*pathname();
  244.     char *ptr;
  245. #endif
  246.  
  247.     ftp = current->cb.ftp;
  248.     if(ftp == NULLFTP){
  249.         printf(notsess);
  250.         return 1;
  251.     }
  252.     remotename = argv[1];
  253. #ifdef MAC
  254.     if(argc < 3) {
  255.         ptr = pathname(applroot,argv[1]);
  256.         strcpy(ftpname,ptr);
  257.         (void)free(ptr);
  258.         localname = ftpname;
  259.     }
  260.     else {
  261.         ptr = pathname(applroot,argv[2]);
  262.         strcpy(ftpname,ptr);
  263.         (void)free(ptr);
  264.         localname = ftpname;
  265.     }
  266. #else
  267.     if(argc < 3)
  268.         localname = remotename;
  269.     else
  270.         localname = argv[2];
  271. #endif
  272.  
  273.     if(ftp->fp != NULLFILE && ftp->fp != stdout)
  274.         fclose(ftp->fp);
  275. #ifdef MAC
  276.     if(ftp->MacBinary && ftp->MBftp)
  277.         if(ftp->mbfp != NULLMBF){
  278.             MBclose(ftp->mbfp);
  279.             free(ftp->mbfp);
  280.             ftp->mbfp = NULLMBF;
  281.             ftp->MBftp = MACB_OFF;
  282.         }
  283. #endif
  284.     ftp->fp = NULLFILE;
  285.  
  286.     if(ftp->type == IMAGE_TYPE)
  287.         mode = binmode[WRITE_BINARY];
  288.     else
  289.         mode = "w";
  290.  
  291. #ifdef MAC
  292.     if(ftp->MacBinary){
  293.         if((ftp->mbfp=MBopen(localname,0,MB_WRITE + (((!ftp->MacBinary) ||
  294.             (ftp->type == ASCII_TYPE)) ? MB_DISABLE : 0))) == NULLMBF) {
  295.             printf(cantwrite,localname);
  296.             return 1;
  297.         }
  298.         ftp->MBftp = MACB_ON;
  299.         ftp->fp = NULLFILE;
  300.     } else
  301. #endif        
  302.     if((ftp->fp = fopen(localname,mode)) == NULLFILE){
  303.         printf(cantwrite,localname);
  304.         return 1;
  305.     }
  306.     ftp->state = RECEIVING_STATE;
  307.     ftpsetup(ftp,ftpdr,NULLVFP,ftpcds);
  308.  
  309.     /* Generate the command to start the transfer */
  310.     return sndftpmsg(ftp,"RETR %s\r\n",remotename);
  311. }
  312. /* List remote directory. Syntax: dir <remote directory/file> [<local name>] */
  313. static
  314. dolist(argc,argv)
  315. int argc;
  316. char *argv[];
  317. {
  318.     void ftpdr(),ftpcds();
  319.     register struct ftp *ftp;
  320. #ifdef MAC
  321.     char temps[256];
  322. #endif
  323.  
  324.     ftp = current->cb.ftp;
  325.     if(ftp == NULLFTP){
  326.         printf(notsess);
  327.         return 1;
  328.     }
  329.     if(ftp->fp != NULLFILE && ftp->fp != stdout)
  330.         fclose(ftp->fp);
  331.     ftp->fp = NULLFILE;
  332. #ifdef MAC
  333.     if(ftp->MacBinary && ftp->MBftp)
  334.         if(ftp->mbfp != NULLMBF){
  335.             MBclose(ftp->mbfp);
  336.             free(ftp->mbfp);
  337.             ftp->mbfp = NULLMBF;
  338.             ftp->MBftp = MACB_OFF;
  339.         }
  340. #endif
  341.     if(argc < 3){
  342.         ftp->fp = stdout;
  343. #ifdef MAC
  344.     } else {
  345.         sprintf(temps,"%s%s",applroot,argv[2]);
  346.         if((ftp->fp = fopen(temps,"w")) == NULLFILE){
  347.             printf(cantwrite,temps);
  348.             return 1;
  349.         }
  350.     }
  351. #else
  352.     } else if((ftp->fp = fopen(argv[2],"w")) == NULLFILE){
  353.         printf(cantwrite,argv[2]);
  354.         return 1;
  355.     }
  356. #endif
  357.     ftp->state = RECEIVING_STATE;
  358.     ftpsetup(ftp,ftpdr,NULLVFP,ftpcds);
  359.     /* Generate the command to start the transfer
  360.      * It's done this way to avoid confusing the 4.2 FTP server
  361.      * if there's no argument
  362.      */
  363.     if(argc > 1)
  364.         return sndftpmsg(ftp,"LIST %s\r\n",argv[1]);
  365.     else
  366.         return sndftpmsg(ftp,"LIST\r\n","");
  367. }
  368. /* Abbreviated (name only) list of remote directory.
  369.  * Syntax: ls <remote directory/file> [<local name>]
  370.  */
  371. static
  372. dols(argc,argv)
  373. int argc;
  374. char *argv[];
  375. {
  376.     void ftpdr(),ftpcds();
  377.     register struct ftp *ftp;
  378. #ifdef MAC
  379.     char temps[256];
  380. #endif
  381.  
  382.     ftp = current->cb.ftp;
  383.     if(ftp == NULLFTP){
  384.         printf(notsess);
  385.         return 1;
  386.     }
  387.     if(ftp->fp != NULLFILE && ftp->fp != stdout)
  388.         fclose(ftp->fp);
  389.     ftp->fp = NULLFILE;
  390. #ifdef MAC
  391.     if(ftp->MacBinary && ftp->MBftp)
  392.         if(ftp->mbfp != NULLMBF){
  393.             MBclose(ftp->mbfp);
  394.             free(ftp->mbfp);
  395.             ftp->mbfp = NULLMBF;
  396.             ftp->MBftp = MACB_OFF;
  397.         }
  398. #endif
  399.     if(argc < 3){
  400.         ftp->fp = stdout;
  401. #ifdef MAC
  402.     } else {
  403.         sprintf(temps,"%s%s",applroot,argv[2]);
  404.         if((ftp->fp = fopen(temps,"w")) == NULLFILE){
  405.             printf(cantwrite,temps);
  406.             return 1;
  407.         }
  408.     }
  409. #else
  410.     } else if((ftp->fp = fopen(argv[2],"w")) == NULLFILE){
  411.         printf(cantwrite,argv[2]);
  412.         return 1;
  413.     }
  414. #endif
  415.     ftp->state = RECEIVING_STATE;
  416.     ftpsetup(ftp,ftpdr,NULLVFP,ftpcds);
  417.     /* Generate the command to start the transfer */
  418.     if(argc > 1)
  419.         return sndftpmsg(ftp,"NLST %s\r\n",argv[1]);
  420.     else
  421.         return sndftpmsg(ftp,"NLST\r\n","");
  422. }
  423. /* Start transmit. Syntax: put <local name> [<remote name>] */
  424. static
  425. doput(argc,argv)
  426. int argc;
  427. char *argv[];
  428. {
  429.     void ftpdt(),ftpcds();
  430.     char *remotename,*localname;
  431.     char *mode;
  432.     struct ftp *ftp;
  433. #ifdef MAC
  434.     char ftpname[256],*pathname();
  435.     char *ptr;
  436. #endif
  437.  
  438.     if((ftp = current->cb.ftp) == NULLFTP){
  439.         printf(notsess);
  440.         return 1;
  441.     }
  442.     localname = argv[1];
  443. #ifdef MAC
  444.     if(argc < 3) {
  445.         remotename = localname;
  446.         ptr = pathname(applroot,localname);
  447.         strcpy(ftpname,ptr);
  448.         (void)free(ptr);
  449.         localname = ftpname;
  450.     }
  451.     else {
  452.         ptr = pathname(applroot,localname);
  453.         strcpy(ftpname,ptr);
  454.         (void)free(ptr);
  455.         localname = ftpname;
  456.         remotename = argv[2];
  457.     }
  458. #else
  459.     if(argc < 3)
  460.         remotename = localname;
  461.     else
  462.         remotename = argv[2];
  463. #endif
  464.  
  465.     if(ftp->fp != NULLFILE && ftp->fp != stdout)
  466.         fclose(ftp->fp);
  467. #ifdef MAC
  468.     if(ftp->MacBinary && ftp->MBftp)
  469.         if(ftp->mbfp != NULLMBF){
  470.             MBclose(ftp->mbfp);
  471.             free(ftp->mbfp);
  472.             ftp->mbfp = NULLMBF;
  473.             ftp->MBftp = MACB_OFF;
  474.         }
  475. #endif
  476.  
  477.     if(ftp->type == IMAGE_TYPE)
  478.         mode = binmode[READ_BINARY];
  479.     else
  480.         mode = "r";
  481.  
  482. #ifdef MAC
  483.     if(ftp->MacBinary){
  484.         if((ftp->mbfp=MBopen(localname,0,MB_READ + (((!ftp->MacBinary) ||
  485.             (ftp->type == ASCII_TYPE)) ? MB_DISABLE : 0))) == NULLMBF) {
  486.             printf(cantread,localname);
  487.             return 1;
  488.         }
  489.         ftp->MBftp = MACB_ON;
  490.         ftp->fp = NULLFILE;
  491.     } else
  492. #endif        
  493.     if((ftp->fp = fopen(localname,mode)) == NULLFILE){
  494.         printf(cantread,localname);
  495.         return 1;
  496.     }
  497.     ftp->state = SENDING_STATE;
  498.     ftpsetup(ftp,NULLVFP,ftpdt,ftpcds);
  499.  
  500.     /* Generate the command to start the transfer */
  501.     return sndftpmsg(ftp,"STOR %s\r\n",remotename);
  502. }
  503. /* Abort a GET or PUT operation in progress. Note: this will leave
  504.  * the partial file on the local or remote system
  505.  */
  506. doabort(argc,argv)
  507. int argc;
  508. char *argv[];
  509. {
  510.     register struct ftp *ftp;
  511.  
  512.     ftp = current->cb.ftp;
  513.  
  514.     /* Close the local file */
  515.     if(ftp->fp != NULLFILE && ftp->fp != stdout)
  516.         fclose(ftp->fp);
  517.     ftp->fp = NULLFILE;
  518. #ifdef MAC
  519.     if(ftp->MacBinary && ftp->MBftp)
  520.         if(ftp->mbfp != NULLMBF){
  521.             MBclose(ftp->mbfp);
  522.             free(ftp->mbfp);
  523.             ftp->mbfp = NULLMBF;
  524.             ftp->MBftp = MACB_OFF;
  525.         }
  526. #endif
  527.     switch(ftp->state){
  528.     case SENDING_STATE:
  529.         /* Send a premature EOF.
  530.          * Unfortunately we can't just reset the connection
  531.          * since the remote side might end up waiting forever
  532.          * for us to send something.
  533.          */
  534.         close_tcp(ftp->data);
  535.         printf("Put aborted\n");
  536.         break;
  537.     case RECEIVING_STATE:
  538.         /* Just exterminate the data channel TCB; this will
  539.          * generate a RST on the next data packet which will
  540.          * abort the sender
  541.          */
  542.         del_tcp(ftp->data);
  543.         ftp->data = NULLTCB;
  544.         printf("Get aborted\n");
  545.         break;
  546.     }
  547.     ftp->state = COMMAND_STATE;
  548.     fflush(stdout);
  549. }
  550. #ifdef MAC
  551. /* Handle "macb" command from user */
  552. static
  553. int
  554. domacb(argc,argv)
  555. int argc;
  556. char *argv[];
  557. {
  558.     register struct ftp *ftp;
  559.  
  560.     ftp = current->cb.ftp;
  561.     if(argc < 2){
  562.         switch(ftp->MacBinary){
  563.         case MACB_OFF:
  564.             printf("MacBinary Disabled\n");
  565.             break;
  566.         case MACB_ON:
  567.             printf("MacBinary Enabled\n");
  568.             break;
  569.         }
  570.         return 0;
  571.     }
  572.     switch(*argv[1]){
  573.     case 'e':
  574.     case 'E':
  575.         if(ftp->type == ASCII_TYPE){
  576.             printf("Must be in image mode first (TYPE I)\n");
  577.             return 1;
  578.         }
  579.         ftp->MacBinary = MACB_ON;
  580.         sndftpmsg(ftp,"MACB E\r\n");
  581.         break;
  582.     case 'd':
  583.     case 'D':
  584.         ftp->MacBinary = MACB_OFF;
  585.         sndftpmsg(ftp,"MACB D\r\n");
  586.         break;
  587.     default:
  588.         printf("Invalid option %s\n",argv[1]);
  589.         return 1;
  590.     }
  591.     return 0;
  592. }
  593. #endif
  594. /* create data port, and send PORT message */
  595. static
  596. ftpsetup(ftp,recv,send,state)
  597. struct ftp *ftp;
  598. void (*send)();
  599. void (*recv)();
  600. void (*state)();
  601. {
  602.     struct socket lsocket;
  603.     struct mbuf *bp;
  604.  
  605.     lsocket.address = ip_addr;
  606.     lsocket.port = lport++;
  607.  
  608.     /* Compose and send PORT a,a,a,a,p,p message */
  609.  
  610.     if((bp = alloc_mbuf(35)) == NULLBUF){    /* 5 more than worst case */
  611.         printf(nospace);
  612.         return;
  613.     }
  614.     /* I know, this looks gross, but it works! */
  615.     sprintf(bp->data,"PORT %u,%u,%u,%u,%u,%u\r\n",
  616.         hibyte(hiword(lsocket.address)),
  617.         lobyte(hiword(lsocket.address)),
  618.         hibyte(loword(lsocket.address)),
  619.         lobyte(loword(lsocket.address)),
  620.         hibyte(lsocket.port),
  621.         lobyte(lsocket.port));
  622.     bp->cnt = strlen(bp->data);
  623.     send_tcp(ftp->control,bp);
  624.  
  625.     /* Post a listen on the data connection */
  626.     ftp->data = open_tcp(&lsocket,NULLSOCK,TCP_PASSIVE,0,
  627.         recv,send,state,0,(char *)ftp);
  628. }
  629. /* FTP Client Control channel Receiver upcall routine */
  630. void
  631. ftpccr(tcb,cnt)
  632. register struct tcb *tcb;
  633. int16 cnt;
  634. {
  635.     struct mbuf *bp;
  636.     struct ftp *ftp;
  637. #ifdef MAC
  638.     char *buf;
  639. #endif
  640.  
  641.     if((ftp = (struct ftp *)tcb->user) == NULLFTP){
  642.         /* Unknown connection; kill it */
  643.         close_tcp(tcb);
  644.         return;
  645.     }
  646.     /* Hold output if we're not the current session */
  647.     if(mode != CONV_MODE || current == NULLSESSION || current->cb.ftp != ftp)
  648.         return;
  649.  
  650.     if(recv_tcp(tcb,&bp,cnt) > 0){
  651.         while(bp != NULLBUF){
  652. #ifdef MAC
  653.             buf = bp->data;
  654.             while(bp->cnt--) {
  655.                 fputc(*buf,stdout);
  656.                 buf++;
  657.             }
  658. #else
  659.              fwrite(bp->data,1,(unsigned)bp->cnt,stdout); 
  660. #endif
  661.             bp = free_mbuf(bp);
  662.         }
  663.         fflush(stdout);
  664.     }
  665. }
  666.  
  667. /* FTP Client Control channel State change upcall routine */
  668. static
  669. void
  670. ftpccs(tcb,old,new)
  671. register struct tcb *tcb;
  672. char old,new;
  673. {
  674.     void ftp_delete();
  675.     struct ftp *ftp;
  676.     char notify = 0;
  677.     extern char *tcpstates[];
  678.     extern char *reasons[];
  679.     extern char *unreach[];
  680.     extern char *exceed[];
  681.  
  682.     /* Can't add a check for unknown connection here, it would loop
  683.      * on a close upcall! We're just careful later on.
  684.      */
  685.     ftp = (struct ftp *)tcb->user;
  686.  
  687.     if(current != NULLSESSION && current->cb.ftp == ftp)
  688.         notify = 1;
  689.  
  690.     switch(new){
  691.     case CLOSE_WAIT:
  692.         if(notify)
  693.             printf("%s\n",tcpstates[new]);
  694.         close_tcp(tcb);
  695.         break;
  696.     case CLOSED:    /* heh heh */
  697.         if(notify){
  698.             printf("%s (%s",tcpstates[new],reasons[tcb->reason]);
  699.             if(tcb->reason == NETWORK){
  700.                 switch(tcb->type){
  701.                 case DEST_UNREACH:
  702.                     printf(": %s unreachable",unreach[tcb->code]);
  703.                     break;
  704.                 case TIME_EXCEED:
  705.                     printf(": %s time exceeded",exceed[tcb->code]);
  706.                     break;
  707.                 }
  708.             }
  709.             printf(")\n");
  710.             cmdmode();
  711.         }
  712.         del_tcp(tcb);
  713.         if(ftp != NULLFTP)
  714.             ftp_delete(ftp);
  715.         break;
  716.     default:
  717.         if(notify)
  718.             printf("%s\n",tcpstates[new]);
  719.         break;
  720.     }
  721.     if(notify)
  722.         fflush(stdout);
  723. }
  724. /* FTP Client Data channel State change upcall handler */
  725. static
  726. void
  727. ftpcds(tcb,old,new)
  728. struct tcb *tcb;
  729. char old,new;
  730. {
  731.     struct ftp *ftp;
  732.  
  733.     if((ftp = (struct ftp *)tcb->user) == NULLFTP){
  734.         /* Unknown connection, kill it */
  735.         close_tcp(tcb);
  736.         return;
  737.     }
  738.     switch(new){
  739.     case FINWAIT2:
  740.     case TIME_WAIT:
  741.         if(ftp->state == SENDING_STATE){
  742.             /* We've received an ack of our FIN, so
  743.              * return to command mode
  744.              */
  745.             ftp->state = COMMAND_STATE;
  746.             if(current != NULLSESSION && current->cb.ftp == ftp){
  747. #ifdef MAC
  748.                 printf("\007Put complete, %lu bytes sent\n",
  749.                     tcb->snd.una - tcb->iss - 2);
  750. #else
  751.                 printf("Put complete, %lu bytes sent\n",
  752.                     tcb->snd.una - tcb->iss - 2);
  753. #endif
  754.                 fflush(stdout);
  755.             }
  756.         }
  757.         break;        
  758.     case CLOSE_WAIT:
  759.         close_tcp(tcb);
  760.         if(ftp->state == RECEIVING_STATE){
  761.             /* End of file received on incoming file */
  762. #ifdef    CPM
  763.             if(ftp->type == ASCII_TYPE)
  764.                 putc(CTLZ,ftp->fp);
  765. #endif
  766. #ifdef MAC
  767.             if(ftp->fp != stdout && ftp->fp != NULLFILE)
  768. #else
  769.             if(ftp->fp != stdout)
  770. #endif
  771.                 fclose(ftp->fp);
  772.             ftp->fp = NULLFILE;
  773. #ifdef MAC
  774.             if(ftp->MacBinary && ftp->MBftp)
  775.                 if(ftp->mbfp != NULLMBF){
  776.                     MBclose(ftp->mbfp);
  777.                     free(ftp->mbfp);
  778.                     ftp->mbfp = NULLMBF;
  779.                     ftp->MBftp = MACB_OFF;
  780.                 }
  781. #endif
  782.             ftp->state = COMMAND_STATE;
  783.             if(current != NULLSESSION && current->cb.ftp == ftp){
  784. #ifdef MAC
  785.                 printf("\007Get complete, %lu bytes received\n",
  786.                     tcb->rcv.nxt - tcb->irs - 2);
  787. #else
  788.                 printf("Get complete, %lu bytes received\n",
  789.                     tcb->rcv.nxt - tcb->irs - 2);
  790. #endif
  791.                 fflush(stdout);
  792.             }
  793.         }
  794.         break;
  795.     case CLOSED:
  796.         ftp->data = NULLTCB;
  797.         del_tcp(tcb);
  798.         break;
  799.     }
  800. }
  801. /* Send a message on the control channel */
  802. /*VARARGS*/
  803. static
  804. int
  805. sndftpmsg(ftp,fmt,arg)
  806. struct ftp *ftp;
  807. char *fmt;
  808. char *arg;
  809. {
  810.     struct mbuf *bp;
  811.     int16 len;
  812.  
  813.     len = strlen(fmt) + strlen(arg) + 10;    /* fudge factor */
  814.     if((bp = alloc_mbuf(len)) == NULLBUF){
  815.         printf(nospace);
  816.         return 1;
  817.     }
  818.     sprintf(bp->data,fmt,arg);
  819.     bp->cnt = strlen(bp->data);
  820.     send_tcp(ftp->control,bp);
  821.     return 0;
  822. }
  823.  
  824.